/**
 * @author Zvonimir Fras <zvonimir@frasbros.com>
 */

#include "./neuron.h"

Neuron::Neuron(QObject* parent) : QObject( parent )
{
   m_w0 = 0.0f;
   m_a = 1;
   m_directInput = false;
   m_outputNeedsUpdate = true;
}

Neuron::Neuron(const Neuron& neuron) : QObject( 0 )
{
   *this = neuron;
}

// shallow copy
// deep copy would require copying the whole network, nooo
Neuron& Neuron::operator=(const Neuron& neuron)
{
   m_error = neuron.m_error;
   m_directInput = neuron.m_directInput;
   m_directValue = neuron.m_directValue;
   m_w0 = neuron.m_w0;
   m_a = neuron.m_a;
   return *this;
}





void Neuron::addPredecessor(Neuron* p)
{
   Synapse *syn = new Synapse( this );
   syn->prependNeuron( p );
   p->m_rightSynapses.append( syn );
   
   syn->appendNeuron( this );
   m_leftSynapses.append( syn );
}


void Neuron::addSuccessor(Neuron* p)
{
   Synapse *syn = new Synapse( this );
   syn->prependNeuron( this );
   m_rightSynapses.append( syn );
   
   syn->appendNeuron( p );
   p->m_leftSynapses.append( syn );
}



void Neuron::directInput(float in)
{
   m_directInput = true;
   if( in != m_directValue ) setOutputNeedsUpdate();
   m_directValue = in;
}

void Neuron::indirectInput()
{
   m_directInput = false;
}




float Neuron::getOutput()
{
   if( m_directInput ) return m_directValue;
   if( !m_outputNeedsUpdate ) return m_output;
   m_output = 1.0f/( 1.0f + pow( M_E, -m_a*m_net() ) );
   return m_output;
}


void Neuron::setOutputNeedsUpdate(bool need)
{
   // if no change, return
   if( m_outputNeedsUpdate == need ) return;
   
   // if doesn't need update, don't tell others, they might need
   if( !need )
   {
      m_outputNeedsUpdate = false;
      return;
   }
   
   // if needs update, tell everybody to the right
   m_outputNeedsUpdate = true;
   
   for( int i = 0; i < m_rightSynapses.size(); i++ )
   {
      Synapse *synapse = m_rightSynapses[i];
      
      synapse->rightNeuron()->setOutputNeedsUpdate( need );
   }
}




float Neuron::m_net()
{
   float retVal = m_w0;
   
   foreach( Synapse *left, m_leftSynapses )
   {
      retVal += left->weight() * left->leftNeuron()->getOutput();
   }
   
   return retVal;
}

